/*
 * AbstractImageDisplay.java
 *
 * Created on 18 avril 2007, 14:20
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package courselog;

import ewe.fx.Color;
import ewe.fx.Graphics;
import ewe.fx.Image;
import ewe.fx.Pen;
import ewe.fx.Point;
import ewe.fx.Rect;
import ewe.sys.Vm;
import ewe.ui.Control;
import ewe.ui.ControlEvent;
import ewe.ui.Event;
import ewe.ui.Gui;
import ewe.ui.IKeys;
import ewe.ui.KeyEvent;
import ewe.ui.PenEvent;
import ewe.ui.mApp;

/**
 * Class providing basic image display features.
 * Allows to move, zoom.
 * @author Courselog
 */
public abstract class AbstractImageDisplay extends Control {

    /**
     * Interval for long press on enter or pen in milliseconds (==500).
     */
    public static final int INTLGPRESSMS = 500;
    /**
     * Interval between double press on enter. in milliseconds (==400)
     */
    public static final int INTDBLPRESSMS = 500;
    /**
     * Display image.
     */
    protected Image dispImage = null;
    /**
     * Modification of Scaling of display image.
     */
    protected double deltaScaleDisplay = 2.0d;
    /**
     * Origin displacement when directionkey is touched.
     */
    protected int imageDelta = 8;
    /**
     * Display image X offset when dragged.
     */
    protected int iOX = 0;
    /**
     * Display image Y offset when dragged.
     */
    protected int iOY = 0;
    /**
     * Time Stamp of enter pressed.
     */
    protected int iEnterPressed = 0;
    /**
     * Number of enter pressed.
     */
    protected int iNumberEnterPressed = 0;
    /**
     * Ticker id when Enter key is pressed.
     */
    protected int tickerForEnter = 0;
    /**
     * KeyEvent of first press on ENTER.
     */
    protected KeyEvent keEnter = null;
    /** Dispatcher of action keys.
     */
    protected ActionKeysDispatcher akdC;
    /**
     * Point of first press on Pen.
     */
    protected Point poPress = new Point();
    /**
     * Time stamp of pen press.
     */
    protected int iStampWhenPressed = 0;
    /**
     * Flag to check if penHeld event took place.
     */
    protected boolean notPenHeld;
    /**
     * Flag to check if penDrag event took place.
     */
    protected boolean notPenDragged;
    /**
     * Point in baseImage for pen actions.
     */
    protected Point poBase = new Point();

    // Class methods
    /**
     * Move picture quickly on the display.
     * @param dx move in x direction.
     * @param dy move in y direction.
     */
    public void moveImage(int dx, int dy) {
        iOX += dx;
        iOY += dy;
        repaint();
    }

    /**
     * Pen is dragged on the image.
     * @param pEv Gives the pen position.
     */
    public void penDrag(PenEvent pEv) {
        int dx = pEv.x - poPress.x;
        int dy = pEv.y - poPress.y;
        if ((dx != 0) || (dy != 0)) {
            notPenDragged = false;
            poPress.set(pEv.x, pEv.y);
            moveImage(dx, dy);
            notPenHeld = true;
        }
    }

    /**
     * This display can handle action keys if this method is called.
     * @param akdC Dispatcher to use. If null, no action.
     */
    public void setActionKeyDispatcher(ActionKeysDispatcher akdC) {
        this.akdC = akdC;
    }

    /**
     * Up button pressed, moves up the origin of the picture.
     * @param kEv Key generating the move.
     */
    public void upButtonPressed(KeyEvent kEv) {
        //CourseLogAppForm.dbgL.addToLog("\n upButtonPressed()");
        moveImage(0, imageDelta);
    }

    /**
     * Down button pressed, moves down the origin of the picture.
     * @param kEv Key generating the move.
     */
    public void downButtonPressed(KeyEvent kEv) {
        //CourseLogAppForm.dbgL.addToLog("\n downButtonPressed()");
        moveImage(0, -imageDelta);
    }

    /**
     * Left button pressed, moves left the origin of the picture.
     * @param kEv Key generating the move.
     */
    public void leftButtonPressed(KeyEvent kEv) {
        //CourseLogAppForm.dbgL.addToLog("\n leftButtonPressed()");
        moveImage(imageDelta, 0);
    }

    /**
     * Right button pressed, moves right the origin of the picture.
     * @param kEv Key generating the move.
     */
    public void rightButtonPressed(KeyEvent kEv) {
        //CourseLogAppForm.dbgL.addToLog("\n rightButtonPressed()");
        moveImage(-imageDelta, 0);
    }

    /**
     * (up,down,left,right) was released. Update completely the image.
     * Calls a updateDisplayedImage(null,0).
     * @param kEv Key generating the move.
     * @see updateDisplayedImage(Rect,int)
     */
    public void directionButtonReleased(KeyEvent kEv) {
        //CourseLogAppForm.dbgL.addToLog("\n directionButtonReleased()");
        updateDisplayedImage(null, 0);
    }

    /**
     * Long press on enter, zoom out of the picture.
     * @param kEv Key generating the move.
     */
    public void longPressEnter(KeyEvent kEv) {
        rescaleImage(1 / deltaScaleDisplay);
        updateDisplayedImage(null, 0);
    }

    /**
     * Short press on enter, does a zoom in of the picture.
     * @param kEv Key generating the move.
     */
    public void shortPressEnter(KeyEvent kEv) {
        //CourseLogAppForm.dbgL.addToLog("\nShortPress on enter");
        rescaleImage(deltaScaleDisplay);
        updateDisplayedImage(null, 0);
    }

    /**
     * Double press on enter, does nothing.
     * Override this to take an action.
     * @param kEv Key generating the move.
     */
    public void doublePressEnter(KeyEvent kEv) {
    //CourseLogAppForm.dbgL.addToLog("\ndouble Press on enter");
    }

    // OVERRIDEN METHODS
    /**
     * Handle properly key end pen events.
     * @param ev Event.
     * @Override ewe.ui.Control.onEvent(Event)
     */
    public void onEvent(Event ev) {
        if (ev instanceof ControlEvent) {
            ControlEvent cEv = (ControlEvent) ev;
            ev.consumed = true;
        } else if (ev instanceof PenEvent) {
            PenEvent pEv = (PenEvent) ev;
            if (pEv.type == PenEvent.PEN_DRAG) {
                penDrag(pEv);
            }
        } else if (ev instanceof KeyEvent) {
            KeyEvent kEv = (KeyEvent) ev;
            if (kEv.key == IKeys.UP) {
                ev.consumed = true;
                if (kEv.type == KeyEvent.KEY_RELEASE) {
                    directionButtonReleased(kEv);
                } else {
                    upButtonPressed(kEv);
                }
            } else if (kEv.key == IKeys.DOWN) {
                ev.consumed = true;
                if (tickerForEnter != 0) {
                    // Small trick, if you press enter and down quickly
                    // you obtain something like a double press of enter.
                    iNumberEnterPressed++;
                } else {
                    if (kEv.type == KeyEvent.KEY_RELEASE) {
                        directionButtonReleased(kEv);
                    } else {
                        downButtonPressed(kEv);
                    }
                }
            } else if (kEv.key == IKeys.LEFT) {
                ev.consumed = true;
                if (kEv.type == KeyEvent.KEY_RELEASE) {
                    directionButtonReleased(kEv);
                } else {
                    leftButtonPressed(kEv);
                }
            } else if (kEv.key == IKeys.RIGHT) {
                ev.consumed = true;
                if (kEv.type == KeyEvent.KEY_RELEASE) {
                    directionButtonReleased(kEv);
                } else {
                    rightButtonPressed(kEv);
                }
            } else if ((kEv.key == 'p') && (kEv.type == KeyEvent.KEY_RELEASE)) {
                // character corresponding to left button
                akdC.onEvent(kEv);
                ev.consumed = true;
            } else if ((kEv.key == 'q') && (kEv.type == KeyEvent.KEY_RELEASE)) {
                // character corresponding to right button
                akdC.onEvent(kEv);
                ev.consumed = true;
            } else if (kEv.key == IKeys.ENTER) {
                ev.consumed = true;
                if (kEv.type == KeyEvent.KEY_PRESS) {
                    if (tickerForEnter == 0) {
                        tickerForEnter = mApp.requestTimer(this, INTDBLPRESSMS);
                        keEnter = kEv;
                        iEnterPressed = kEv.timeStamp;
                        iNumberEnterPressed = 0;
                    //CourseLogAppForm.dbgL.addToLog(" tick "+tickerForEnter);
                    }
                    iNumberEnterPressed++;
                //CourseLogAppForm.dbgL.addToLog(" nbE "+iNumberEnterPressed);
                //CourseLogAppForm.dbgL.addToLog(" at "+kEv.timeStamp);
                } else {
                    //CourseLogAppForm.dbgL.addToLog("\n Enter Released");
                    //CourseLogAppForm.dbgL.addToLog(" after "+(kEv.timeStamp-iEnterPressed));
                    if ((iEnterPressed > 0) && ((kEv.timeStamp - iEnterPressed) > INTLGPRESSMS)) {
                        mApp.cancelTimer(tickerForEnter);
                        //CourseLogAppForm.dbgL.addToLog(" timer cancelled "+tickerForEnter);
                        tickerForEnter = 0;
                        iEnterPressed = 0;
                        keEnter = null;
                        longPressEnter(keEnter);
                    }
                }
            } else if (kEv.key == ' ') {
                ev.consumed = true;
                if (kEv.type == KeyEvent.KEY_PRESS) {
                    longPressEnter(kEv);
                }
            } else {
                ev.consumed = false;
            }
        }
        //repaint();
        if (!ev.consumed) {
            super.onEvent(ev);
        }
    }

    /**
     * Paint this control.
     * @param g Graphic context.
     * @param r rectangle to repaint.
     */
    public void doPaint(Graphics g, Rect r) {
        doBackground(g);
        if (dispImage != null) {
            //CourseLogAppForm.dbgL.addToLog("\nrepaint ("+iOX+","+iOY+") ["+dispImage.getWidth()+
            //        ","+dispImage.getHeight()+"]");
            g.drawImage(dispImage, iOX, iOY);
        }
    //CourseLogAppForm.dbgL.getLogControl().repaint();
    }

    /**
     * Get the tick for the double press on enter, otherwise, pass to super.
     * @param id the event of the tick.
     * @param elapsed milliseconds elapsed.
     */
    @Override
    public void ticked(int id, int elapsed) {
        if (id == tickerForEnter) {
            //CourseLogAppForm.dbgL.addToLog("\n tickerForEnter ("+tickerForEnter+") after "+elapsed);
            tickerForEnter = 0;
            if((keEnter.modifiers&IKeys.SHIFT)!=0) {
                iNumberEnterPressed=0; // emulate a long press in case of shift-enter
                longPressEnter(keEnter);
            } else if ((Vm.getAsyncKeyState(IKeys.ENTER) & IKeys.KEY_PRESSED) != 0) {
            //CourseLogAppForm.dbgL.addToLog(" Enter still Pressed");
            } else if (iNumberEnterPressed == 1) {
                iEnterPressed = 0;
                shortPressEnter(keEnter);
            } else if (iNumberEnterPressed == 2) {
                iEnterPressed = 0;
                doublePressEnter(keEnter);
            } else { // In case of key repeat, it works too
                iEnterPressed = 0;
                longPressEnter(keEnter);
            }
        } else {
            super.ticked(id, elapsed);
        }
    }

    /**
     * When pen is released, either it is the end of a click (short or long), 
     * either it is the end of a drag.
     * Call the appropriate methods or update the image if it has been dragged.
     */
    public void penReleased(Point p) {
        //CourseLogAppForm.dbgL.addToLog("\n penReleased ("+p.x+"-"+poPress.x+","+p.y+"-"+poPress.y+")");
        super.penReleased(p);
        if (notPenDragged) {
            if (((Vm.getTimeStamp() - iStampWhenPressed) < INTLGPRESSMS) && (notPenHeld)) {
                poBase.set(p.x, p.y);
                actionOnPenClick(poBase);
            } else {
            //      penHeld(p);
            }
        } else {
            updateDisplayedImage(null, 0);
        }
    }

    /**
     * When pen is pressed on the image, the image moves with it.
     */
    public void penHeld(Point p) {
        //CourseLogAppForm.dbgL.addToLog("\n penHeld("+p.x+","+p.y+")");
        notPenHeld = false;
        //super.penHeld(p);
        poBase.set(p.x, p.y);
        actionOnPenHeld(poBase);
    }

    /**
     * When pen is pressed on the image, the image moves with it.
     * @param p point where it was pressed.
     */
    public void penPressed(Point p) {
        //CourseLogAppForm.dbgL.addToLog("\n penPressed("+p.x+","+p.y+")");
        notPenHeld = true;
        notPenDragged = true;
        iStampWhenPressed = Vm.getTimeStamp();
        poPress.set(p);
        super.penPressed(p);
    }

// ABSTRACT METHODS
    public abstract void actionOnPenHeld(Point p);

    public abstract void actionOnPenClick(Point p);

    public abstract void updateDisplayedImage(Rect r, int opt);

    /**
     * Rescale the displayed image by a given factor.
     * This is not supposed to update the displayed image, only to apply some factor.
     */
    public abstract void rescaleImage(double byFactor);
}
